package utility

import (
	"bytes"
	"container/list"
	"encoding/binary"
	"net"
	"time"
)

type ICMP struct {
	Type        uint8
	Code        uint8
	Checksum    uint16
	Identifier  uint16
	SequenceNum uint16
}

func Ping(ip string) (string, int64, error) {
	var (
		icmp     ICMP
		laddr    = net.IPAddr{IP: net.ParseIP("0.0.0.0")}
		raddr, _ = net.ResolveIPAddr("ip", ip)
	)
	conn, err := net.DialIP("ip4:icmp", &laddr, raddr)
	if err != nil {
		return raddr.String(), 0, err
	}
	defer conn.Close()

	//构造ICMP报文
	icmp.Type = 8
	icmp.Code = 0
	icmp.Checksum = 0
	icmp.Identifier = 0
	icmp.SequenceNum = 0

	var buffer bytes.Buffer
	binary.Write(&buffer, binary.BigEndian, icmp)
	icmp.Checksum = CheckSum(buffer.Bytes())
	buffer.Reset()
	binary.Write(&buffer, binary.BigEndian, icmp)

	recv := make([]byte, 1024)

	statistic := list.New()
	sended_packets := 0

	for i := 4; i > 0; i-- {
		if _, err := conn.Write(buffer.Bytes()); err != nil {
			return raddr.String(), 0, err
		}
		sended_packets++
		t_start := time.Now()

		conn.SetReadDeadline((time.Now().Add(time.Second * 2)))
		conn.Read(recv)

		t_end := time.Now()

		dur := t_end.Sub(t_start).Nanoseconds() / 1e6

		statistic.PushBack(dur)
	}

	var min, max, sum, average int64
	//信息统计
	if statistic.Len() == 0 {
		min, max, sum = 0, 0, 0
	} else {
		min, max, sum = statistic.Front().Value.(int64), statistic.Front().Value.(int64), int64(0)
	}

	for v := statistic.Front(); v != nil; v = v.Next() {

		val := v.Value.(int64)

		switch {
		case val < min:
			min = val
		case val > max:
			max = val
		}

		sum = sum + val
	}
	average = int64(float32(sum) / float32(statistic.Len()))
	return raddr.String(), average, nil
}

func CheckSum(data []byte) uint16 {
	var (
		sum    uint32
		length int = len(data)
		index  int
	)
	for length > 1 {
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		index += 2
		length -= 2
	}
	if length > 0 {
		sum += uint32(data[index])
	}
	sum += (sum >> 16)

	return uint16(^sum)
}

func GetIpAddr(value string) string {
	raddr, _ := net.ResolveIPAddr("ip", value)
	return raddr.String()
}
